﻿/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#ifndef SHARED_DBC_DBCSTORE_H_
#define SHARED_DBC_DBCSTORE_H_

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif

#include <cstdint>
#include <list>
#include <boost/unordered_map.hpp>
#include <NodeCpp/Macros.h>
#include "Shared/DBC/DBCLoader.h"

class DBCStorage
{
    typedef std::list<char*> StringPoolList;
public:
    explicit DBCStorage(const char* _Fmt)
        : RecordCount_(0), FieldCount_(0)
        , Format_(_Fmt), IndexTable_(NULL)
        , DataTable_(NULL), Data_()
        , Loaded_(false), StringPoolList_() { }
    ~DBCStorage(void) { Clear(); }

    std::uint32_t  GetNumRows(void) const { return Loaded_ ? Data_.size() : RecordCount_; }
    const char* GetFormat(void) const { return Format_; }
    std::uint32_t GetFieldCount(void) const { return FieldCount_; }

    template<class T>
    const T* LookupEntry(std::uint32_t _ID) const
    {
        if (Loaded_)
        {
            typename boost::unordered_map<std::uint32_t, const char*>::const_iterator _Iter = Data_.find(_ID);
            if (_Iter != Data_.end()) {
                return reinterpret_cast<const T*>(_Iter->second);
            }
        }
        return (_ID >= RecordCount_) ? NULL : reinterpret_cast<const T*>(IndexTable_[_ID]);
    }

    template<class LoaderType>
    bool Load(char const* _Filename)
    {
        LoaderType _Loader;
        // 加载数据
        if (!_Loader.Load(_Filename, Format_)) {
            return false;
        }

        FieldCount_ = _Loader.GetCols();
        DataTable_ = _Loader.AutoProduceData(Format_, RecordCount_, (char**&)IndexTable_);
        StringPoolList_.push_back(_Loader.AutoProduceStrings(Format_, (char*)DataTable_));
        return IndexTable_ != NULL;
    }

    template<class T>
    void SetEntry(std::uint32_t _ID, T* _T) // Cryptic they say..
    {
        if (!Loaded_)
        {
            for (std::uint32_t _I = 0; _I < RecordCount_; ++_I)
            {
                T const* _Node = LookupEntry<T>(_I);
                if (!_Node)
                    continue;
                Data_[_I] = (char*)_Node;
            }
            Loaded_ = true;
        }
        Data_[_ID] = (char*)_T;
    }

    template<class LoaderType>
    bool LoadStringsFrom(const char* _Filename)
    {
        // 必须先调用Load函数加载数据
        if (!IndexTable_) {
            return false;
        }

        LoaderType _Loader;
        // 加载数据
        if (!_Loader.Load(_Filename, Format_)) {
            return false;
        }

        // 获取字符串数据
        StringPoolList_.push_back(_Loader.AutoProduceStrings(Format_, (char*)DataTable_));
        return true;
    }

    void Clear()
    {
        if (Loaded_)
        {
            Data_.clear();
            Loaded_ = false;
        }

        if (!IndexTable_)
        {
            return;
        }

        delete[]((char*)IndexTable_);
        IndexTable_ = NULL;
        delete[]((char*)DataTable_);
        DataTable_ = NULL;

        while (!StringPoolList_.empty())
        {
            delete[] StringPoolList_.front();
            StringPoolList_.pop_front();
        }
        RecordCount_ = 0;
    }

    void EraseEntry(std::uint32_t _ID)
    {
        NODECPP_ASSERT(_ID < RecordCount_ && "Entry to be erased must be in bounds!");
        IndexTable_[_ID] = NULL;
    }
    template<class T>
    void InsertEntry(T* _Entry, std::uint32_t _ID)
    {
        NODECPP_ASSERT(_ID < RecordCount_ && "Entry to be inserted must be in bounds!");
        IndexTable_[_ID] = (char*)_Entry;
    }

private:
    std::uint32_t RecordCount_;                           /* 记录数 */
    std::uint32_t FieldCount_;                            /* 每记录字段数 */
    char const* Format_;                                  /* 记录格式 */
    char** IndexTable_;                                   /* 索引表 */
    char* DataTable_;                                     /* 数据表 */
    boost::unordered_map<std::uint32_t, const char*> Data_;
    bool Loaded_;                                         /* 数据是否加载到HashMap中 */
    StringPoolList StringPoolList_;                       /* 字符串数据 */
};

#endif /* SHARED_DBC_DBCSTORE_H_ */
